package cn.tangshh.universal.core.handler;

import cn.hutool.core.collection.CollUtil;
import cn.tangshh.universal.core.api.DR;
import cn.tangshh.universal.core.api.R;
import cn.tangshh.universal.core.api.RCode;
import cn.tangshh.universal.core.exception.BusinessException;
import cn.tangshh.universal.core.exception.ControlledException;
import cn.tangshh.universal.core.exception.LimitRequestException;
import cn.tangshh.universal.core.util.RequestUtil;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.ConstraintViolationException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.util.StreamUtils;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.resource.NoResourceFoundException;

import java.io.IOException;
import java.nio.charset.Charset;

/**
 * 全局异常处理程序
 *
 * @author Tang
 * @version v1.0
 * @since 2024/05/31
 */
@Slf4j
@Order(9)
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 请求方式不支持
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public R<?> httpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e, HttpServletRequest request) {
        String uri = RequestUtil.getReqCurlUri(request);
        log.warn("[HttpRequestMethodNotSupportedException] uri:'{}' 不支持'{}'请求", uri, e.getMethod());
        return DR.of(RCode.METHOD_NOT_SUPPORTED);
    }

    /**
     * 资源不存在
     */
    @ExceptionHandler(NoResourceFoundException.class)
    public R<?> noResourceFoundException(HttpServletRequest request) {
        String uri = RequestUtil.getReqCurlUri(request);
        log.warn("[NoResourceFoundException] uri:'{}' 资源不存在", uri);
        return DR.of(RCode.NOT_FOUND);
    }

    /**
     * 请求数据转换失败
     */
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public R<?> httpMessageNotReadableException(HttpServletRequest request) {
        String uri = RequestUtil.getReqCurlUri(request);
        String content = "......";
        try {
            content = StreamUtils.copyToString(request.getInputStream(), Charset.defaultCharset());
            content = content.replaceAll("[\r\n ]", "").replaceAll("\"([^\"]{30})[^\"]+\"", "\"$1...\"");
        } catch (IOException ignore) {}
        log.warn("[HttpMessageNotReadableException] uri:'{}' context:'{}' 入参格式不正确", uri, content);
        return DR.fail("sys.error.param_format");
    }

    /**
     * 自定义验证异常
     */
    @ExceptionHandler(BindException.class)
    public R<?> bindException(BindException e, HttpServletRequest request) {
        String uri = RequestUtil.getReqCurlUri(request);
        FieldError first = CollUtil.getFirst(e.getFieldErrors());
        log.warn("[BindException] uri:'{}' 参数'{}:{}'校验未通过", uri, first.getField(), first.getRejectedValue());
        return DR.fail(first.getDefaultMessage());
    }

    /**
     * 参数约束异常
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public R<?> constraintViolationException(ConstraintViolationException e, HttpServletRequest request) {
        String uri = RequestUtil.getReqCurlUri(request);
        R<?> fail = DR.fail(e.getMessage());
        log.warn("[ConstraintViolationException] uri:'{}' 参数不符合约束条件：{}", uri, fail.getMsg());
        return fail;
    }

    /**
     * 业务异常
     */
    @ExceptionHandler(BusinessException.class)
    public R<?> businessException(BusinessException e, HttpServletRequest request) {
        String uri = RequestUtil.getReqCurlUri(request);
        R<?> fail = DR.fail(e.getMessage());
        log.info("[BusinessException] uri:'{}' 错误信息：{}", uri, fail.getMsg());
        return fail;
    }

    /**
     * 限流异常
     */
    @ExceptionHandler(LimitRequestException.class)
    public R<?> limitRequestException(LimitRequestException e, HttpServletRequest request) {
        String uri = RequestUtil.getReqCurlUri(request);
        log.info("[LimitRequestException] uri:'{}' 触发限流.", uri);
        return DR.fail(e.getMessage());
    }

    /**
     * 受控的异常
     */
    @ExceptionHandler(ControlledException.class)
    public R<?> exception(ControlledException e, HttpServletRequest request) {
        String uri = RequestUtil.getReqCurlUri(request);
        R<?> fail = DR.fail(e.getMessage());
        log.warn("[{}] uri:'{}' {}.", e.getClass().getSimpleName(), uri, fail.getMsg());
        return fail;
    }

    /**
     * 系统异常
     */
    @ExceptionHandler(Exception.class)
    public R<?> exception(Exception e, HttpServletRequest request) {
        String uri = RequestUtil.getReqCurlUri(request);
        log.error("[{}] uri:'{}' 发生未预料异常.", e.getClass().getSimpleName(), uri, e);
        return DR.of(RCode.FAIL);
    }
}